package com.wyk.project.mq.consumer;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wyk.project.common.convention.exception.ServiceException;
import com.wyk.project.dao.entity.*;
import com.wyk.project.dao.mapper.*;
import com.wyk.project.dto.biz.ShortLinkStatsRecordDTO;
import com.wyk.project.mq.idempotent.MessageQueueIdenmpotentHandler;
import com.wyk.project.util.LinkUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RReadWriteLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static com.wyk.project.common.constant.RedisKeyConstant.LOCK_GID_UPDATE_KEY;
import static com.wyk.project.common.constant.ShortLinkConstant.AMAP_REMOTE_URL;

@Slf4j
@Component
@RequiredArgsConstructor
public class ShortLinkStatsSaveConsumer{

    private final ShortLinkMapper shortLinkMapper;
    private final ShortLinkGotoMapper shortLinkGotoMapper;
    private final RedissonClient redissonClient;
    private final LinkAccessStatsMapper linkAccessStatsMapper;
    private final LinkLocaleStatsMapper linkLocaleStatsMapper;
    private final LinkOsStatsMapper linkOsStatsMapper;
    private final LinkBrowserStatsMapper linkBrowserStatsMapper;
    private final LinkAccessLogsStatsMapper linkAccessLogsMapper;
    private final LinkDeviceStatsMapper linkDeviceStatsMapper;
    private final LinkNetworkStatsMapper linkNetworkStatsMapper;
    private final LinkStatsTodayMapper linkStatsTodayMapper;
    private final StringRedisTemplate stringRedisTemplate;
    private final MessageQueueIdenmpotentHandler messageQueueIdenmpotentHandler;

    @Value("${short-link.stats.locale.amap-key}")
    private String statsLocaleAmapKey;

    @RabbitListener(queues = "short-link-queue")
    public void onMessage(Map<String, String> producerMap){
        String keys = producerMap.get("keys");
        // 判断是否被消费过 第一次就是 ！true
        if (!messageQueueIdenmpotentHandler.isMessageProcessed(keys)){
            // 判断消费流程是否执行完（避免出现因为一些极端原因（如断电），导致实际上没有消费但是判断消费的情况）
            if (messageQueueIdenmpotentHandler.isAccomplish(keys)){
                return;
            }
            throw new ServiceException("消息未完成流程，需要消息队列进行重试");
        }
        // 使用throwable是怕出现有的异常无法通过exception进行捕捉
        try {
                ShortLinkStatsRecordDTO statsRecordDTO = JSON.parseObject(producerMap.get("statsRecord"), ShortLinkStatsRecordDTO.class);
                actualSaveShortLinkStats(statsRecordDTO);

        } catch (Throwable throwable){
            // 某某某情况宕机了
            messageQueueIdenmpotentHandler.delMessageProcessed(keys);
            throw throwable;
        }
        // 设置消息流程执行完成
        messageQueueIdenmpotentHandler.setAccomplish(keys);
    }

    public void actualSaveShortLinkStats(ShortLinkStatsRecordDTO statsRecord) {
        String fullShortUrl = statsRecord.getFullShortUrl();
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock(String.format(LOCK_GID_UPDATE_KEY, fullShortUrl));
        //先拿读锁
        RLock rLock = readWriteLock.readLock();
        rLock.lock();
        try {
            LambdaQueryWrapper<ShortLinkGotoDO> queryWrapper = Wrappers.lambdaQuery(ShortLinkGotoDO.class)
                    .eq(ShortLinkGotoDO::getFullShortUrl, fullShortUrl);
            ShortLinkGotoDO shortLinkGotoDO = shortLinkGotoMapper.selectOne(queryWrapper);
            String gid = shortLinkGotoDO.getGid();
            int hour = DateUtil.hour(new Date(),true);
            int week = LocalDateTime.now().getDayOfWeek().getValue();
            //监控 pv uv uip
            LinkAccessStatsDO linkAccessStatsDO = LinkAccessStatsDO.builder()
                    .pv(1)
                    .uv(statsRecord.getUvFirstFlag() ? 1 : 0)
                    .uip(statsRecord.getUipFirstFlag() ? 1 : 0)
                    .hour(hour)
                    .weekday(week)
                    .date(new Date())
                    .fullShortUrl(fullShortUrl)
                    .build();
            linkAccessStatsMapper.shortLinkStats(linkAccessStatsDO);
            //监控 地区
            String actualCountry = LinkUtil.getActualLocale(statsRecord.getRemoteAddr()).split("\\|")[0];
            String actualProvince = LinkUtil.getActualLocale(statsRecord.getRemoteAddr()).split("\\|")[1];
            String actualCity = LinkUtil.getActualLocale(statsRecord.getRemoteAddr()).split("\\|")[2];
            //地区
            LinkLocaleStatsDO linkLocaleStatsDO = LinkLocaleStatsDO.builder()
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .province(actualProvince)
                    .city(actualCity)
                    // .adcode(unknowFlag ? "未知":jsonObject.getString("adcode"))
                    .country(actualCountry)
                    .cnt(1)
                    .build();

            linkLocaleStatsMapper.shortLinkLocaleStats(linkLocaleStatsDO);
            //操作系统
            LinkOsStatsDO linkOsStatsDO = LinkOsStatsDO.builder()
                    .os(statsRecord.getOs())
                    .cnt(1)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkOsStatsMapper.shortLinkOsStats(linkOsStatsDO);
            //浏览器
            LinkBrowserStatsDO linkBrowserStatsDO = LinkBrowserStatsDO.builder()
                    .browser(statsRecord.getBrowser())
                    .cnt(1)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkBrowserStatsMapper.shortlinkBrowserStats(linkBrowserStatsDO);
            //设备
            LinkDeviceStatsDO linkDeviceStatsDO = LinkDeviceStatsDO.builder()
                    .device(statsRecord.getDevice())
                    .cnt(1)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkDeviceStatsMapper.shortlinkDeviceStats(linkDeviceStatsDO);
            //网络
            LinkNetworkStatsDO linkNetworkStatsDO = LinkNetworkStatsDO.builder()
                    .network(statsRecord.getNetwork())
                    .cnt(1)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkNetworkStatsMapper.shortlinkNetworkStats(linkNetworkStatsDO);
            //高频 访问 ip信息
            LinkAccessLogsDO linkAccessLogsDO = LinkAccessLogsDO.builder()
                    .user(statsRecord.getUv())
                    .ip(statsRecord.getRemoteAddr())
                    .browser(statsRecord.getBrowser())
                    .os(statsRecord.getOs())
                    .network(statsRecord.getNetwork())
                    .device(statsRecord.getDevice())
                    .locale(StrUtil.join("-", "中国", actualProvince, actualCity))
                    .fullShortUrl(fullShortUrl)
                    .build();
            linkAccessLogsMapper.insert(linkAccessLogsDO);
            // t_link 的历史数据自增
            shortLinkMapper.incrementStats(gid, fullShortUrl, 1, statsRecord.getUvFirstFlag() ? 1 : 0, statsRecord.getUipFirstFlag() ? 1 : 0);
            //todo 一天的时间刷新
            // 今日访问数据
            LinkStatsTodayDO linkStatsTodayDO = LinkStatsTodayDO.builder()
                    .todayPv(1)
                    .todayUv(statsRecord.getUvFirstFlag() ? 1 : 0)
                    .todayUip(statsRecord.getUipFirstFlag() ? 1 : 0)
                    .fullShortUrl(fullShortUrl)
                    .date(new Date())
                    .build();
            linkStatsTodayMapper.shortlinkTodayStats(linkStatsTodayDO);
        } catch (Throwable ex) {
            log.error("短链接访问量统计异常", ex);
        }
        finally {
            rLock.unlock();
        }
    }

}